你真的了解符号化么?
本文字数:3946字
预计阅读时间:8分钟
iOS下的符号化
前情提要
❝关于符号化,我想iOS开发的相关人员并不陌生,也在日常的开发中也经常打交道,网上关于符号化的文章可以说是漫天飞舞,但并没有一篇文章可以说的很全面,于是便有了这篇文章的诞生,当然这也是笔者在工作中的一些总结学习,如果对文章有任何的问题请联系:
weiniu@sohu-inc.com
以下文章中的符号化均是针对iOS下的,例子也是iOS下的Project
目录表
符号化是什么 符号化的程度 为什么要符号化 崩溃文件解析 Crash日志各部分分析 符号化流程 符号化所需文件 符号化工具 symbolicatecrash atos 扩伸延展 参考文档
符号化是什么
关于符号化 符号化从通俗意义上讲就是把一些机器语言可以转化成人类可读的符号,而在这里的环境下就是指iOS或者Mac OS下的一些异常信息(十六进制符号表示)通过某些手段转化成开发人员可读的高级代码片段,从而进一步定位异常的来源,迅速修复
符号化的程度
完全符号化
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libswiftCore.dylib 0x00000001bd38da70 specialized _fatalErrorMessage+ 2378352 (_:_:file:line:flags:) + 384
1 libswiftCore.dylib 0x00000001bd38da70 specialized _fatalErrorMessage+ 2378352 (_:_:file:line:flags:) + 384
2 libswiftCore.dylib 0x00000001bd15958c _ArrayBuffer._checkInoutAndNativeTypeCheckedBounds+ 66956 (_:wasNativeTypeChecked:) + 200
3 libswiftCore.dylib 0x00000001bd15c814 Array.subscript.getter + 88
4 TouchCanvas 0x00000001022cbfa8 Line.updateRectForExistingPoint(_:) (in TouchCanvas) + 656
5 TouchCanvas 0x00000001022c90b0 Line.updateWithTouch(_:) (in TouchCanvas) + 464
6 TouchCanvas 0x00000001022e7374 CanvasView.updateEstimatedPropertiesForTouches(_:) (in TouchCanvas) + 708
7 TouchCanvas 0x00000001022df754 ViewController.touchesEstimatedPropertiesUpdated(_:) (in TouchCanvas) + 304
8 TouchCanvas 0x00000001022df7e8 @objc ViewController.touchesEstimatedPropertiesUpdated(_:) (in TouchCanvas) + 120
9 UIKitCore 0x00000001b3da6230 forwardMethod1 + 136
10 UIKitCore 0x00000001b3da6230 forwardMethod1 + 136
11 UIKitCore 0x00000001b3e01e24 -[_UIEstimatedTouchRecord dispatchUpdateWithPressure:stillEstimated:] + 340
部分符号化
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libswiftCore.dylib 0x00000001bd38da70 specialized _fatalErrorMessage+ 2378352 (_:_:file:line:flags:) + 384
1 libswiftCore.dylib 0x00000001bd38da70 specialized _fatalErrorMessage+ 2378352 (_:_:file:line:flags:) + 384
2 libswiftCore.dylib 0x00000001bd15958c _ArrayBuffer._checkInoutAndNativeTypeCheckedBounds+ 66956 (_:wasNativeTypeChecked:) + 200
3 libswiftCore.dylib 0x00000001bd15c814 Array.subscript.getter + 88
4 TouchCanvas 0x00000001022cbfa8 0x1022c0000 + 49064
5 TouchCanvas 0x00000001022c90b0 0x1022c0000 + 37040
6 TouchCanvas 0x00000001022e7374 0x1022c0000 + 160628
7 TouchCanvas 0x00000001022df754 0x1022c0000 + 128852
8 TouchCanvas 0x00000001022df7e8 0x1022c0000 + 129000
9 UIKitCore 0x00000001b3da6230 forwardMethod1 + 136
10 UIKitCore 0x00000001b3da6230 forwardMethod1 + 136
11 UIKitCore 0x00000001b3e01e24 -[_UIEstimatedTouchRecord dispatchUpdateWithPressure:stillEstimated:] + 340
未符号化
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libswiftCore.dylib 0x00000001bd38da70 0x1bd149000 + 2378352
1 libswiftCore.dylib 0x00000001bd38da70 0x1bd149000 + 2378352
2 libswiftCore.dylib 0x00000001bd15958c 0x1bd149000 + 66956
3 libswiftCore.dylib 0x00000001bd15c814 0x1bd149000 + 79892
4 TouchCanvas 0x00000001022cbfa8 0x1022c0000 + 49064
5 TouchCanvas 0x00000001022c90b0 0x1022c0000 + 37040
6 TouchCanvas 0x00000001022e7374 0x1022c0000 + 160628
7 TouchCanvas 0x00000001022df754 0x1022c0000 + 128852
8 TouchCanvas 0x00000001022df7e8 0x1022c0000 + 129000
9 UIKitCore 0x00000001b3da6230 0x1b3348000 + 10871344
10 UIKitCore 0x00000001b3da6230 0x1b3348000 + 10871344
11 UIKitCore 0x00000001b3e01e24 0x1b3348000 + 11247140
为什么要符号化
通过上一个问题可以得知,为了让开发人员可以快速找到异常堆栈,从而定位问题,解决问题,保证程序的良好运行
崩溃文件解析
工具善其事,必先利其器。你想解决某件事,你首先得清楚这件事是如何构成的,以及每个部分代表什么,接下来我们来聊一聊崩溃日志文件中的各个字段的含义
下面是某个项目中的一段原始崩溃日志截取
Incident Identifier: xxxxx-xxxx-xxxx-xxxx-89D2A9086DFE
CrashReporter Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Hardware Model: iPhone10,3
Process: bdnews [22785]
Path: /private/var/containers/Bundle/Application/7FFC365D-8DBE-4B2E-9E6B-372047CA2A7A/bdnews.app/bdnews
Identifier: com.bd.newspaper.inhouse
Version: 224.1 (6.6.20)
Code Type: ARM-64 (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: com.bd.newspaper.inhouse [2953]
Date/Time: 2021-07-08 08:04:46.6235 +0800
Launch Time: 2021-07-08 07:12:05.1466 +0800
OS Version: iPhone OS 14.1 (18A8395)
Release Type: User
Baseband Version: 6.02.01
Report Version: 104
Exception Type: EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d
Termination Description: SPRINGBOARD, <RBSTerminateContext| domain:10 code:0x8BADF00D explanation:scene-update watchdog transgression: application<com.bd.newspaper.inhouse>:22785 exhausted real (wall clock) time allowance of 10.00 seconds | ProcessVisibility: Foreground | ProcessState: Running | WatchdogEvent: scene-update | WatchdogVisibility: Background | WatchdogCPUStatistics: ( | "Elapsed total CPU time (seconds): 6.730 (user 6.730, system 0.000), 11% CPU", | "Elapsed application CPU time (seconds): 0.448, 1% CPU" | ) reportType:CrashLog maxTerminationResistance:Interactive>
Triggered by Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x00000001b8d48900 semaphore_wait_trap + 8
1 libdispatch.dylib 0x000000018de4ec3c _dispatch_sema4_wait$VARIANT$armv81 + 24
2 libdispatch.dylib 0x000000018de4f28c _dispatch_semaphore_wait_slow + 128
3 CFNetwork 0x000000018e89fddc CFURLConnectionSendSynchronousRequest + 352
4 CFNetwork 0x000000018e81d9cc 0x18e81a000 + 14796
5 Foundation 0x000000018f4008a0 -[NSData+ 247968 (NSData) initWithContentsOfURL:] + 228
6 ImageIO 0x000000018f6ff110 IIOImageSource::IIOImageSource+ 655632 (__CFURL const*, IIODictionary*) + 608
7 ImageIO 0x000000018f703f98 CGImageSourceCreateWithURL + 196
8 bdunews 0x0000000104a2d620 0x10412c000 + 9442848
9 bdnews 0x0000000104a2d43c 0x10412c000 + 9442364
10 bdnews 0x0000000105511c68 0x10412c000 + 20864104
11 bdnews 0x00000001055109cc 0x10412c000 + 20859340
12 bdnews 0x0000000104442d50 0x10412c000 + 3239248
13 bdnews 0x0000000104b6a3ac 0x10412c000 + 10740652
14 bdnews 0x0000000104b6c658 0x10412c000 + 10749528
15 bdnews 0x0000000104b669fc 0x10412c000 + 10725884
16 bdnews 0x0000000104be62bc 0x10412c000 + 11248316
17 bdnews 0x0000000104be4980 0x10412c000 + 11241856
18 bdnews 0x0000000105acfd44 0x10412c000 + 26885444
19 bdnews 0x0000000105aea2dc 0x10412c000 + 26993372
20 libdispatch.dylib 0x000000018de7d298 _dispatch_call_block_and_release + 24
21 libdispatch.dylib 0x000000018de7e280 _dispatch_client_callout + 16
22 libdispatch.dylib 0x000000018de60608 _dispatch_main_queue_callback_4CF$VARIANT$armv81 + 936
23 CoreFoundation 0x000000018e1c4c30 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
24 CoreFoundation 0x000000018e1bf0e8 __CFRunLoopRun + 2480
25 CoreFoundation 0x000000018e1be200 CFRunLoopRunSpecific + 572
26 GraphicsServices 0x00000001a433b598 GSEventRunModal + 160
27 UIKitCore 0x0000000190a87bcc -[UIApplication _run] + 1052
28 UIKitCore 0x0000000190a8d1a0 UIApplicationMain + 164
29 bdnews 0x0000000105310464 0x10412c000 + 18760804
30 libdyld.dylib 0x000000018de9d588 start + 4
...
Binary Images:
0x10412c000 - 0x106f0ffff sohunews arm64 <255d5329b68c378a834d829abcfdbd42> /var/containers/Bundle/Application/7FFC365D-8DBE-4B2E-9E6B-372047CA2A7A/sohunews.app/sohunews
0x109c60000 - 0x109c6bfff libobjc-trampolines.dylib arm64 <b6698616837a37cf85d2ed2d2d868269> /usr/lib/libobjc-trampolines.dylib
0x109cc8000 - 0x109d33fff dyld arm64 <90ec9373b0653c5b986074202c172829> /usr/lib/dyld
0x109db4000 - 0x109e1ffff RevealServer arm64 <aececcc618bf3b508d470d26907c12bf> /var/containers/Bundle/Application/7FFC365D-8DBE-4B2E-9E6B-372047CA2A7A/sohunews.app/Frameworks/RevealServer.framework/RevealServer
...
Crash日志各部分分析
Crash日志信息头
Incident Identifier:崩溃日志的唯一标识符,两份报告从来不会相同 CrashReporter Key:崩溃日志的关键字 Hardware Model: 设备类型,iPhone10,3 Process:进程名称,通常是ipa中可执行文件的名称,对应Xcode项目中的"product name" Path:设备存储中应用可执行文件的路径 Identifier: 应用的bundle id Version: 应用的Build (Version),也就是CFBundleVersion Code Type: 可执行文件对应的CPU架构类型,这里是ARM64处理器 Role:在应用终止的时分配给该进程的任务角色,对于分析报告来说,这个字段没有用 Parent Process: 父进程,通常是launch id之类的,如果是连接xcode调试则可能是debug process之类的 Coalition:包含该应用程序的进程的联盟的名称 Date/Time: 崩溃的时间 OS Version: 设备操作系统版本 Report Version: Crash report的版本,不同版本crash信息格式有些差别 Exception Type:异常类型, EXC_CRASH (SIGKILL)
,这段代码解读为,在mach
内核环境下发生exc_crash
的异常,然后被unix
系统下的sigkill
信号捕获Exception Codes: 关于异常的处理器特定信息,编码为一个或多个64位十六进制数字。通常情况下,这个字段不会出现,因为操作系统在本部分的其他字段中把信息作为可读的信息 Exception Subtype: 异常代码的可读部分 Exception Message: 从异常代码中提取的可读信息 Exception Note: 不是特定于一种异常类型的附加信息。如果这个字段中包含了 EXC_CORPSE_NOTIFY
,那么这个崩溃并不是来自于硬件陷阱(Trap),要么是因为进程被操作系统终止,要么是进程调用了abort()
,如果这个字段包含了SIMULATED
这不是一个崩溃,进程没有崩溃,但是操作系统随后可能要求终止该进程,如果这个字段包含NON-FATAL CONDITION
,这也不是一个崩溃,进程没有终止,因为产生崩溃报告得到的问题并不是致命的。Termination Reason: 当操作系统终止一个进程时指定退出的原因信息。关键的操作系统组件,包括进程内部和外部,在遇到致命错误时终止进程,并在这个字段中记录原因。你可以在这个字段中找到相关信息,比如一个无效的代码签名,一个缺失的依赖库,或者在没有相关字符串的情况下访问隐私信息等 Triggered by Thread or Crashed Thread: 发生异常的线程 崩溃栈 (从左到右)
0.调用栈序号 1.对应二进制的镜像(image)名称,包含应用的可执行文件,系统动态库,framework中的二进制文件等 2.调用函数在相应镜像文件中的地址,这里也包含三部分(从左到右) 2.0.调用函数的地址,十六进制数字组成 2.1.可执行指令部分相对镜像文件中的起始加载地址,十六进制数字组成 2.2.函数的地址地相对于加载地址的偏移量,由十进制组成,其实这的关系就是这部分的地址+第二部分地址等于第一部分地址 镜像文件信息,分为六个部分 (从左到右)
二进制图像在进程中的地址范围。该地址是二进制的加载地址 这个地址是和上一个地址组成了二进制图像的进程地址范围 镜像文件的名称 CPU架构类型,跟信息头的Code Type表示一样 镜像文件的UUID,这个是镜像文件的唯一标识,同时也是对应的符号文件的UUID,可以用来判断符号文件是否正确 镜像文件在设备中的路径
符号化流程
符号化所需文件
符号化需要崩溃日志文件,dSYM文件以及符号化工具,这3个元素缺一不可,你需要提前准备好,任何第三方的工具也都需要这些元素,只是表现形式不同而已
崩溃文件
文件表现形式:通常崩溃文件都是以 xxx.crash
结尾的文件呈现获取方式:获取的方式分调试开发以及线上进行不同的方式获取,首先如果是开发测试阶段,可以通过 Xcode
编译工具从测试手机导出,当然你也可以先自行在目标手机上看是否存在你需要的崩溃,查看方式就是设置-隐私-分析与改进-分析数据
这个文件下就是你整部手机的一些异常信息,也是Jetsam机制产生的(有兴趣可以继续深入了解下Jetsam机制,这个与本文无关,暂且忽略哈)开发测试导出 Xcode-Window-Devices and simulators-View Device Logs
(左侧工具栏选中你要导出的目的设备)线上获取导出 如果是线上问题出现了,比如一些可以被信号量捕获的问题,就可以在 Xcode
以相同的方式导出,但是还有一些不能捕获的崩溃就需要我们从代码层面进行控制,然后存储上报,具体方式根据自己的业务进行制定即可。dSYM文件
dSYM是一个调试符号文件(debug symbols file),它是在你的项目中构建设置中启用 Strip Debug Symbols
开启时生成的。生成流程:当这个设置 Strip Debug Symbols
开启时,你的对象的符号名称会从编译后的二进制文件中删除,这个是为了防止黑客对你的代码进行逆向工程而采取的措施之一,该文件每次编译时都会发生变化,因为时间戳不同,与项目设置无关作用:崩溃文件只是告诉你了内存的虚拟地址和加载地址,dSYM文件则辅助的定位到了对象的调用堆栈,并允许开发者可读 如何获取dSYM,分为 Bitcode Enabled
和Bitcode disabled
Bitcode Enabled
你首先要连接到 App Store Connect
打开App详情页面 点击活动 从 "所有构建 "列表中,选择一个与崩溃相匹配的版本 点击dSYM的下载链接 Bitcode disabled
打开Xcode 点击Window > Organizer 从 "所有构建 "列表中,选择一个与崩溃相匹配的版本 右击并点击 Show in Finder
在 .xcarchive
右击文件并点击显示包内容
发现 <app_name>.app.dSYM
在dSYM
文件下发现该文件
符号化工具
symbolicatecrash
symbolicatecrash工具的查找
❝该工具是
Apple
提供的一个命令行工具,你通过以下的方式找到它
备注:以下所有xxx
代表工程名字
// find symbolicatecrash for Applications
$ sudo find /Applications -name symbolicatecrash
/**
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/iOSSupport/Library/PrivateFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
/Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/symbolicatecrash
/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/symbolicatecrash
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/symbolicatecrash
/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
*/
// find the path which contains DVTFoundation.framework and copy the path to the clipboard
// Set the DEVELOPER_DIR environment variable. Write this command
$ export DEVELOPER_DIR=”/Applications/Xcode.app/Contents/Developer”
// The last step process
// This tool takes two arguments. The first argument is the path to your crash file. The second argument is the path to the dSYM file. So, the final command may look like
// run the tool
$symbolicatecrash -d <path_to_dsym> -o <path_for_symbolicated_crash> <path_to_crash_report>
$/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash xxx.crash xxxApp.dSYM > Custom.crash
❝For symbolicatecrash detail use /path/symbolicatecrash --help
符号解析无效果
❝如果在符号化过程中没有效果或者无法符号化,那就要比较你当前的dSYM的
uuid
是否和崩溃文件中镜像文件部分列出的构建uuid
相匹配
// find uuid for dSYM
$ dwarfdump –uuid xxxApp.dSYM/Contents/Resources/DWARF/xxxApp
/**
// It looks that
UUID: 1A36EBE0-AE8D-3A2F-8D76-2B786C707305 (armv7) xxx.app.dSYM/Contents/Resources/DWARF/xxx
UUID: 9B8D679D-C302-31DA-9573-A8D5A6588627 (arm64) xxx.app.dSYM/Contents/Resources/DWARF/xxx
*/
// find uuid for Binary Images
$ dwarfdump --uuid <PathToBinary>
$ dwarfdump --uuid /usr/lib/libobjc-trampolines.dylib
/**
// It looks that
UUID: 1BFE69F2-6C6C-3E82-A8CA-781EE7C87C4B (x86_64) /usr/lib/libobjc-trampolines.dylib
UUID: 361143B8-E66E-3402-85B5-C20893AAB9C9 (x86_64h) /usr/lib/libobjc-trampolines.dylib
UUID: 3358BF92-85AC-3174-9344-C99A5EA74E6B (arm64e) /usr/lib/libobjc-trampolines.dylib
// The resulf of run all platoms for uuid,then check someone platom of crash log and dSYM's uuid
*/
atos
关于atos
atos命令将十六进制地址转换为源代码中可识别的函数名称和行号 命令格式
atos -arch <Binary Architecture> -o <Path to dSYM file>/Contents/Resources/DWARF/<binary image name> -l <load address> <address to symbolicate>
// explain parameters
load adress:可执行指令部分相对镜像文件中的起始加载地址
address to symbolicate:调用函数的地址
atos -o xxx.app.dSYM/Contents/Resources/DWARF/xxx -l 0x00000001c4fe7000 -arch arm64
atos -arch arm64 -o xxx.app.dSYM/Contents/Resources/DWARF/xxx -l 0x00000001c4fe7000 0x00000001a2d6e29c
// extension about xcrun
// xcrun can accept address to symbolicate on the way in command line.
xcrun atos -o xxx.app.dSYM/Contents/Resources/DWARF/xxx -l 0x1040dc000 -arch arm64
0x1052c0464
__63-[GCDAsyncUdpSocket asyncResolveHost:port:withCompletionBlock:]_block_invoke.118 (in xxx) (GCDAsyncUdpSocket.m:1209)
0x104ba9094
-[BDAboutController tapButton:atIndex:] (in xxx) (BDAboutController.m:251)
扩伸延展
关于一些第三方的APM(Application Performance Management),即应用性能管理
目前的一些APM都是基于以上原理进行符号解析,但是由于这样或者那样的原因导致某些解析是有出入,所以需要你二次确认,这需要你在日常工作中有敏锐的观察力,毕竟工具是人写的不可能不出错,在这里就不列举了,在用的小伙伴可以自行校验 关于第三方解析工具
目前暂无推荐,可以自己搜索关键词进行尝试,目前笔者是使用了自己编写脚本语言进行符号化,参考资料也已给出
参考文档
https://developer.apple.com/documentation/xcode/adding-identifiable-symbol-names-to-a-crash-report
https://developer.apple.com/documentation/xcode/examining-the-fields-in-a-crash-report
https://stackoverflow.com/questions/22460058/how-is-a-dsym-file-created/22460268
https://github.com/adam-roth/litesymbols
后记
❝符号化在平常不过了,但是需要在熟练的基础上再去考虑如何压缩时间,提高效率,快速定位问题。该篇只是入门,符号化之路还有很多坑等着大家,祝大家填坑愉快。
也许你还想看
(▼点击文章标题或封面查看)
2021-09-09
2021-08-26
2021-08-19
2021-08-12
2021-08-05